// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_PARSING_PARSER_H_
#define V8_PARSING_PARSER_H_

#include <cstddef>

#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/base/compiler-specific.h"
#include "src/base/threaded-list.h"
#include "src/globals.h"
#include "src/parsing/parser-base.h"
#include "src/parsing/parsing.h"
#include "src/parsing/preparser.h"
#include "src/pointer-with-payload.h"
#include "src/zone/zone-chunk-list.h"

namespace v8 {

class ScriptCompiler;

namespace internal {

    class ConsumedPreparseData;
    class ParseInfo;
    class ParserTarget;
    class ParserTargetScope;
    class PendingCompilationErrorHandler;
    class PreparseData;

    // ----------------------------------------------------------------------------
    // JAVASCRIPT PARSING

    class Parser;

    struct ParserFormalParameters : FormalParametersBase {
        struct Parameter : public ZoneObject {
            Parameter(Expression* pattern, Expression* initializer, int position,
                int initializer_end_position, bool is_rest)
                : initializer_and_is_rest(initializer, is_rest)
                , pattern(pattern)
                , position(position)
                , initializer_end_position(initializer_end_position)
            {
            }

            PointerWithPayload<Expression, bool, 1> initializer_and_is_rest;

            Expression* pattern;
            Expression* initializer() const
            {
                return initializer_and_is_rest.GetPointer();
            }
            int position;
            int initializer_end_position;
            inline bool is_rest() const { return initializer_and_is_rest.GetPayload(); }

            Parameter* next_parameter = nullptr;
            bool is_simple() const
            {
                return pattern->IsVariableProxy() && initializer() == nullptr && !is_rest();
            }

            const AstRawString* name() const
            {
                DCHECK(is_simple());
                return pattern->AsVariableProxy()->raw_name();
            }

            Parameter** next() { return &next_parameter; }
            Parameter* const* next() const { return &next_parameter; }
        };

        void set_strict_parameter_error(const Scanner::Location& loc,
            MessageTemplate message)
        {
            strict_error_loc = loc;
            strict_error_message = message;
        }

        bool has_duplicate() const { return duplicate_loc.IsValid(); }
        void ValidateDuplicate(Parser* parser) const;
        void ValidateStrictMode(Parser* parser) const;

        explicit ParserFormalParameters(DeclarationScope* scope)
            : FormalParametersBase(scope)
        {
        }

        base::ThreadedList<Parameter> params;
        Scanner::Location duplicate_loc = Scanner::Location::invalid();
        Scanner::Location strict_error_loc = Scanner::Location::invalid();
        MessageTemplate strict_error_message = MessageTemplate::kNone;
    };

    template <>
    struct ParserTypes<Parser> {
        using Base = ParserBase<Parser>;
        using Impl = Parser;

        // Return types for traversing functions.
        using Block = v8::internal::Block*;
        using BreakableStatement = v8::internal::BreakableStatement*;
        using ClassLiteralProperty = ClassLiteral::Property*;
        using ClassPropertyList = ZonePtrList<ClassLiteral::Property>*;
        using Expression = v8::internal::Expression*;
        using ExpressionList = ScopedPtrList<v8::internal::Expression>;
        using FormalParameters = ParserFormalParameters;
        using ForStatement = v8::internal::ForStatement*;
        using FunctionLiteral = v8::internal::FunctionLiteral*;
        using Identifier = const AstRawString*;
        using IterationStatement = v8::internal::IterationStatement*;
        using ObjectLiteralProperty = ObjectLiteral::Property*;
        using ObjectPropertyList = ScopedPtrList<v8::internal::ObjectLiteralProperty>;
        using Statement = v8::internal::Statement*;
        using StatementList = ScopedPtrList<v8::internal::Statement>;
        using Suspend = v8::internal::Suspend*;

        // For constructing objects returned by the traversing functions.
        using Factory = AstNodeFactory;

        // Other implementation-specific functions.
        using FuncNameInferrer = v8::internal::FuncNameInferrer;
        using SourceRange = v8::internal::SourceRange;
        using SourceRangeScope = v8::internal::SourceRangeScope;
        using Target = ParserTarget;
        using TargetScope = ParserTargetScope;
    };

    class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
    public:
        explicit Parser(ParseInfo* info);
        ~Parser()
        {
            delete reusable_preparser_;
            reusable_preparser_ = nullptr;
        }

        static bool IsPreParser() { return false; }

        void ParseOnBackground(ParseInfo* info);

        // Initializes an empty scope chain for top-level scripts, or scopes which
        // consist of only the native context.
        void InitializeEmptyScopeChain(ParseInfo* info);

        // Deserialize the scope chain prior to parsing in which the script is going
        // to be executed. If the script is a top-level script, or the scope chain
        // consists of only a native context, maybe_outer_scope_info should be an
        // empty handle.
        //
        // This only deserializes the scope chain, but doesn't connect the scopes to
        // their corresponding scope infos. Therefore, looking up variables in the
        // deserialized scopes is not possible.
        void DeserializeScopeChain(Isolate* isolate, ParseInfo* info,
            MaybeHandle<ScopeInfo> maybe_outer_scope_info,
            Scope::DeserializationMode mode = Scope::DeserializationMode::kScopesOnly);

        // Move statistics to Isolate
        void UpdateStatistics(Isolate* isolate, Handle<Script> script);
        void HandleSourceURLComments(Isolate* isolate, Handle<Script> script);

    private:
        friend class ParserBase<Parser>;
        friend struct ParserFormalParameters;
        friend class i::ExpressionScope<ParserTypes<Parser>>;
        friend class i::VariableDeclarationParsingScope<ParserTypes<Parser>>;
        friend class i::ParameterDeclarationParsingScope<ParserTypes<Parser>>;
        friend class i::ArrowHeadParsingScope<ParserTypes<Parser>>;
        friend bool v8::internal::parsing::ParseProgram(ParseInfo*, Isolate*);
        friend bool v8::internal::parsing::ParseFunction(
            ParseInfo*, Handle<SharedFunctionInfo> shared_info, Isolate*);

        bool AllowsLazyParsingWithoutUnresolvedVariables() const
        {
            return scope()->AllowsLazyParsingWithoutUnresolvedVariables(
                original_scope_);
        }

        bool parse_lazily() const { return mode_ == PARSE_LAZILY; }
        enum Mode { PARSE_LAZILY,
            PARSE_EAGERLY };

        class ParsingModeScope {
        public:
            ParsingModeScope(Parser* parser, Mode mode)
                : parser_(parser)
                , old_mode_(parser->mode_)
            {
                parser_->mode_ = mode;
            }
            ~ParsingModeScope() { parser_->mode_ = old_mode_; }

        private:
            Parser* parser_;
            Mode old_mode_;
        };

        // Runtime encoding of different completion modes.
        enum CompletionKind {
            kNormalCompletion,
            kThrowCompletion,
            kAbruptCompletion
        };

        Variable* NewTemporary(const AstRawString* name)
        {
            return scope()->NewTemporary(name);
        }

        void PrepareGeneratorVariables();

        // Returns nullptr if parsing failed.
        FunctionLiteral* ParseProgram(Isolate* isolate, ParseInfo* info);

        FunctionLiteral* ParseFunction(Isolate* isolate, ParseInfo* info,
            Handle<SharedFunctionInfo> shared_info);
        FunctionLiteral* DoParseFunction(Isolate* isolate, ParseInfo* info,
            const AstRawString* raw_name);

        // Called by ParseProgram after setting up the scanner.
        FunctionLiteral* DoParseProgram(Isolate* isolate, ParseInfo* info);

        // Parse with the script as if the source is implicitly wrapped in a function.
        // We manually construct the AST and scopes for a top-level function and the
        // function wrapper.
        void ParseWrapped(Isolate* isolate, ParseInfo* info,
            ScopedPtrList<Statement>* body, DeclarationScope* scope,
            Zone* zone);

        ZonePtrList<const AstRawString>* PrepareWrappedArguments(Isolate* isolate,
            ParseInfo* info,
            Zone* zone);

        PreParser* reusable_preparser()
        {
            if (reusable_preparser_ == nullptr) {
                reusable_preparser_ = new PreParser(
                    &preparser_zone_, &scanner_, stack_limit_, ast_value_factory(),
                    pending_error_handler(), runtime_call_stats_, logger_, -1,
                    parsing_module_, parsing_on_main_thread_);
#define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name());
                SET_ALLOW(natives);
                SET_ALLOW(harmony_public_fields);
                SET_ALLOW(harmony_static_fields);
                SET_ALLOW(harmony_dynamic_import);
                SET_ALLOW(harmony_import_meta);
                SET_ALLOW(harmony_private_fields);
                SET_ALLOW(harmony_private_methods);
                SET_ALLOW(eval_cache);
#undef SET_ALLOW
                preparse_data_buffer_.reserve(128);
            }
            return reusable_preparser_;
        }

        void ParseModuleItemList(ScopedPtrList<Statement>* body);
        Statement* ParseModuleItem();
        const AstRawString* ParseModuleSpecifier();
        void ParseImportDeclaration();
        Statement* ParseExportDeclaration();
        Statement* ParseExportDefault();
        void ParseExportStar();
        struct ExportClauseData {
            const AstRawString* export_name;
            const AstRawString* local_name;
            Scanner::Location location;
        };
        ZoneChunkList<ExportClauseData>* ParseExportClause(
            Scanner::Location* reserved_loc);
        struct NamedImport : public ZoneObject {
            const AstRawString* import_name;
            const AstRawString* local_name;
            const Scanner::Location location;
            NamedImport(const AstRawString* import_name, const AstRawString* local_name,
                Scanner::Location location)
                : import_name(import_name)
                , local_name(local_name)
                , location(location)
            {
            }
        };
        ZonePtrList<const NamedImport>* ParseNamedImports(int pos);
        Statement* BuildInitializationBlock(DeclarationParsingResult* parsing_result);
        void DeclareLabel(ZonePtrList<const AstRawString>** labels,
            ZonePtrList<const AstRawString>** own_labels,
            VariableProxy* expr);
        bool ContainsLabel(ZonePtrList<const AstRawString>* labels,
            const AstRawString* label);
        Expression* RewriteReturn(Expression* return_value, int pos);
        Statement* RewriteSwitchStatement(SwitchStatement* switch_statement,
            Scope* scope);
        Block* RewriteCatchPattern(CatchInfo* catch_info);
        void ReportVarRedeclarationIn(const AstRawString* name, Scope* scope);
        Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
            const SourceRange& catch_range,
            Block* finally_block,
            const SourceRange& finally_range,
            const CatchInfo& catch_info, int pos);
        void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind,
            ScopedPtrList<Statement>* body);
        void ParseAndRewriteAsyncGeneratorFunctionBody(
            int pos, FunctionKind kind, ScopedPtrList<Statement>* body);
        void DeclareFunctionNameVar(const AstRawString* function_name,
            FunctionLiteral::FunctionType function_type,
            DeclarationScope* function_scope);

        Statement* DeclareFunction(const AstRawString* variable_name,
            FunctionLiteral* function, VariableMode mode,
            VariableKind kind, int beg_pos, int end_pos,
            ZonePtrList<const AstRawString>* names);
        Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name);
        Variable* CreatePrivateNameVariable(ClassScope* scope,
            const AstRawString* name);
        FunctionLiteral* CreateInitializerFunction(
            const char* name, DeclarationScope* scope,
            ZonePtrList<ClassLiteral::Property>* fields);

        bool IdentifierEquals(const AstRawString* identifier,
            const AstRawString* other)
        {
            return identifier == other;
        }

        Statement* DeclareClass(const AstRawString* variable_name, Expression* value,
            ZonePtrList<const AstRawString>* names,
            int class_token_pos, int end_pos);
        void DeclareClassVariable(const AstRawString* name, ClassInfo* class_info,
            int class_token_pos);
        void DeclareClassProperty(ClassScope* scope, const AstRawString* class_name,
            ClassLiteralProperty* property, bool is_constructor,
            ClassInfo* class_info);
        void DeclareClassField(ClassScope* scope, ClassLiteralProperty* property,
            const AstRawString* property_name, bool is_static,
            bool is_computed_name, bool is_private,
            ClassInfo* class_info);
        Expression* RewriteClassLiteral(ClassScope* block_scope,
            const AstRawString* name,
            ClassInfo* class_info, int pos, int end_pos);
        Statement* DeclareNative(const AstRawString* name, int pos);

        Block* IgnoreCompletion(Statement* statement);

        Scope* NewHiddenCatchScope();

        bool HasCheckedSyntax()
        {
            return scope()->GetDeclarationScope()->has_checked_syntax();
        }

        // PatternRewriter and associated methods defined in pattern-rewriter.cc.
        friend class PatternRewriter;
        void InitializeVariables(
            ScopedPtrList<Statement>* statements, VariableKind kind,
            const DeclarationParsingResult::Declaration* declaration);

        Block* RewriteForVarInLegacy(const ForInfo& for_info);
        void DesugarBindingInForEachStatement(ForInfo* for_info, Block** body_block,
            Expression** each_variable);
        Block* CreateForEachStatementTDZ(Block* init_block, const ForInfo& for_info);

        Statement* DesugarLexicalBindingsInForStatement(
            ForStatement* loop, Statement* init, Expression* cond, Statement* next,
            Statement* body, Scope* inner_scope, const ForInfo& for_info);

        FunctionLiteral* ParseFunctionLiteral(
            const AstRawString* name, Scanner::Location function_name_location,
            FunctionNameValidity function_name_validity, FunctionKind kind,
            int function_token_position, FunctionLiteral::FunctionType type,
            LanguageMode language_mode,
            ZonePtrList<const AstRawString>* arguments_for_wrapped_function);

        ObjectLiteral* InitializeObjectLiteral(ObjectLiteral* object_literal)
        {
            object_literal->CalculateEmitStore(main_zone());
            return object_literal;
        }

        bool IsPropertyWithPrivateFieldKey(Expression* property);

        // Insert initializer statements for var-bindings shadowing parameter bindings
        // from a non-simple parameter list.
        void InsertShadowingVarBindingInitializers(Block* block);

        // Implement sloppy block-scoped functions, ES2015 Annex B 3.3
        void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope);

        void DeclareUnboundVariable(const AstRawString* name, VariableMode mode,
            InitializationFlag init, int pos);
        V8_WARN_UNUSED_RESULT
        VariableProxy* DeclareBoundVariable(const AstRawString* name,
            VariableMode mode, int pos);
        void DeclareAndBindVariable(VariableProxy* proxy, VariableKind kind,
            VariableMode mode, InitializationFlag init,
            Scope* declaration_scope, bool* was_added,
            int begin, int end = kNoSourcePosition);
        V8_WARN_UNUSED_RESULT
        Variable* DeclareVariable(const AstRawString* name, VariableKind kind,
            VariableMode mode, InitializationFlag init,
            Scope* declaration_scope, bool* was_added,
            int begin, int end = kNoSourcePosition);
        void Declare(Declaration* declaration, const AstRawString* name,
            VariableKind kind, VariableMode mode, InitializationFlag init,
            Scope* declaration_scope, bool* was_added, int var_begin_pos,
            int var_end_pos = kNoSourcePosition);

        bool TargetStackContainsLabel(const AstRawString* label);
        BreakableStatement* LookupBreakTarget(const AstRawString* label);
        IterationStatement* LookupContinueTarget(const AstRawString* label);

        // Factory methods.
        FunctionLiteral* DefaultConstructor(const AstRawString* name, bool call_super,
            int pos, int end_pos);

        // Skip over a lazy function, either using cached data if we have it, or
        // by parsing the function with PreParser. Consumes the ending }.
        // In case the preparser detects an error it cannot identify, it resets the
        // scanner- and preparser state to the initial one, before PreParsing the
        // function.
        // SkipFunction returns true if it correctly parsed the function, including
        // cases where we detect an error. It returns false, if we needed to stop
        // parsing or could not identify an error correctly, meaning the caller needs
        // to fully reparse. In this case it resets the scanner and preparser state.
        bool SkipFunction(const AstRawString* function_name, FunctionKind kind,
            FunctionLiteral::FunctionType function_type,
            DeclarationScope* function_scope, int* num_parameters,
            int* function_length,
            ProducedPreparseData** produced_preparsed_scope_data);

        Block* BuildParameterInitializationBlock(
            const ParserFormalParameters& parameters);
        Block* BuildRejectPromiseOnException(Block* block);

        void ParseFunction(
            ScopedPtrList<Statement>* body, const AstRawString* function_name,
            int pos, FunctionKind kind, FunctionLiteral::FunctionType function_type,
            DeclarationScope* function_scope, int* num_parameters,
            int* function_length, bool* has_duplicate_parameters,
            int* expected_property_count, int* suspend_count,
            ZonePtrList<const AstRawString>* arguments_for_wrapped_function);

        void ThrowPendingError(Isolate* isolate, Handle<Script> script);

        class TemplateLiteral : public ZoneObject {
        public:
            TemplateLiteral(Zone* zone, int pos)
                : cooked_(8, zone)
                , raw_(8, zone)
                , expressions_(8, zone)
                , pos_(pos)
            {
            }

            const ZonePtrList<const AstRawString>* cooked() const { return &cooked_; }
            const ZonePtrList<const AstRawString>* raw() const { return &raw_; }
            const ZonePtrList<Expression>* expressions() const { return &expressions_; }
            int position() const { return pos_; }

            void AddTemplateSpan(const AstRawString* cooked, const AstRawString* raw,
                int end, Zone* zone)
            {
                DCHECK_NOT_NULL(raw);
                cooked_.Add(cooked, zone);
                raw_.Add(raw, zone);
            }

            void AddExpression(Expression* expression, Zone* zone)
            {
                expressions_.Add(expression, zone);
            }

        private:
            ZonePtrList<const AstRawString> cooked_;
            ZonePtrList<const AstRawString> raw_;
            ZonePtrList<Expression> expressions_;
            int pos_;
        };

        using TemplateLiteralState = TemplateLiteral*;

        TemplateLiteralState OpenTemplateLiteral(int pos);
        // "should_cook" means that the span can be "cooked": in tagged template
        // literals, both the raw and "cooked" representations are available to user
        // code ("cooked" meaning that escape sequences are converted to their
        // interpreted values). Invalid escape sequences cause the cooked span
        // to be represented by undefined, instead of being a syntax error.
        // "tail" indicates that this span is the last in the literal.
        void AddTemplateSpan(TemplateLiteralState* state, bool should_cook,
            bool tail);
        void AddTemplateExpression(TemplateLiteralState* state,
            Expression* expression);
        Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start,
            Expression* tag);

        ArrayLiteral* ArrayLiteralFromListWithSpread(
            const ScopedPtrList<Expression>& list);
        Expression* SpreadCall(Expression* function,
            const ScopedPtrList<Expression>& args, int pos,
            Call::PossiblyEval is_possibly_eval);
        Expression* SpreadCallNew(Expression* function,
            const ScopedPtrList<Expression>& args, int pos);
        Expression* RewriteSuperCall(Expression* call_expression);

        void SetLanguageMode(Scope* scope, LanguageMode mode);
        void SetAsmModule();

        Expression* RewriteSpreads(ArrayLiteral* lit);

        Expression* BuildInitialYield(int pos, FunctionKind kind);
        Assignment* BuildCreateJSGeneratorObject(int pos, FunctionKind kind);

        // Generic AST generator for throwing errors from compiled code.
        Expression* NewThrowError(Runtime::FunctionId function_id,
            MessageTemplate message, const AstRawString* arg,
            int pos);

        Statement* CheckCallable(Variable* var, Expression* error, int pos);

        void RewriteAsyncFunctionBody(ScopedPtrList<Statement>* body, Block* block,
            Expression* return_value);

        void AddArrowFunctionFormalParameters(ParserFormalParameters* parameters,
            Expression* params, int end_pos);
        void SetFunctionName(Expression* value, const AstRawString* name,
            const AstRawString* prefix = nullptr);

        // Helper functions for recursive descent.
        V8_INLINE bool IsEval(const AstRawString* identifier) const
        {
            return identifier == ast_value_factory()->eval_string();
        }

        V8_INLINE bool IsAsync(const AstRawString* identifier) const
        {
            return identifier == ast_value_factory()->async_string();
        }

        V8_INLINE bool IsArguments(const AstRawString* identifier) const
        {
            return identifier == ast_value_factory()->arguments_string();
        }

        V8_INLINE bool IsEvalOrArguments(const AstRawString* identifier) const
        {
            return IsEval(identifier) || IsArguments(identifier);
        }

        // Returns true if the expression is of type "this.foo".
        V8_INLINE static bool IsThisProperty(Expression* expression)
        {
            DCHECK_NOT_NULL(expression);
            Property* property = expression->AsProperty();
            return property != nullptr && property->obj()->IsThisExpression();
        }

        // This returns true if the expression is an indentifier (wrapped
        // inside a variable proxy).  We exclude the case of 'this', which
        // has been converted to a variable proxy.
        V8_INLINE static bool IsIdentifier(Expression* expression)
        {
            VariableProxy* operand = expression->AsVariableProxy();
            return operand != nullptr && !operand->is_new_target();
        }

        V8_INLINE static const AstRawString* AsIdentifier(Expression* expression)
        {
            DCHECK(IsIdentifier(expression));
            return expression->AsVariableProxy()->raw_name();
        }

        V8_INLINE VariableProxy* AsIdentifierExpression(Expression* expression)
        {
            return expression->AsVariableProxy();
        }

        V8_INLINE bool IsConstructor(const AstRawString* identifier) const
        {
            return identifier == ast_value_factory()->constructor_string();
        }

        V8_INLINE bool IsName(const AstRawString* identifier) const
        {
            return identifier == ast_value_factory()->name_string();
        }

        V8_INLINE static bool IsBoilerplateProperty(
            ObjectLiteral::Property* property)
        {
            return !property->IsPrototype();
        }

        V8_INLINE bool IsNative(Expression* expr) const
        {
            DCHECK_NOT_NULL(expr);
            return expr->IsVariableProxy() && expr->AsVariableProxy()->raw_name() == ast_value_factory()->native_string();
        }

        V8_INLINE static bool IsArrayIndex(const AstRawString* string,
            uint32_t* index)
        {
            return string->AsArrayIndex(index);
        }

        // Returns true if the statement is an expression statement containing
        // a single string literal.  If a second argument is given, the literal
        // is also compared with it and the result is true only if they are equal.
        V8_INLINE bool IsStringLiteral(Statement* statement,
            const AstRawString* arg = nullptr) const
        {
            ExpressionStatement* e_stat = statement->AsExpressionStatement();
            if (e_stat == nullptr)
                return false;
            Literal* literal = e_stat->expression()->AsLiteral();
            if (literal == nullptr || !literal->IsString())
                return false;
            return arg == nullptr || literal->AsRawString() == arg;
        }

        V8_INLINE void GetDefaultStrings(const AstRawString** default_string,
            const AstRawString** dot_default_string)
        {
            *default_string = ast_value_factory()->default_string();
            *dot_default_string = ast_value_factory()->dot_default_string();
        }

        // Functions for encapsulating the differences between parsing and preparsing;
        // operations interleaved with the recursive descent.
        V8_INLINE void PushLiteralName(const AstRawString* id)
        {
            fni_.PushLiteralName(id);
        }

        V8_INLINE void PushVariableName(const AstRawString* id)
        {
            fni_.PushVariableName(id);
        }

        V8_INLINE void PushPropertyName(Expression* expression)
        {
            if (expression->IsPropertyName()) {
                fni_.PushLiteralName(expression->AsLiteral()->AsRawPropertyName());
            } else {
                fni_.PushLiteralName(ast_value_factory()->computed_string());
            }
        }

        V8_INLINE void PushEnclosingName(const AstRawString* name)
        {
            fni_.PushEnclosingName(name);
        }

        V8_INLINE void AddFunctionForNameInference(FunctionLiteral* func_to_infer)
        {
            fni_.AddFunction(func_to_infer);
        }

        V8_INLINE void InferFunctionName() { fni_.Infer(); }

        // If we assign a function literal to a property we pretenure the
        // literal so it can be added as a constant function property.
        V8_INLINE static void CheckAssigningFunctionLiteralToProperty(
            Expression* left, Expression* right)
        {
            DCHECK_NOT_NULL(left);
            if (left->IsProperty() && right->IsFunctionLiteral()) {
                right->AsFunctionLiteral()->set_pretenure();
            }
        }

        // A shortcut for performing a ToString operation
        V8_INLINE Expression* ToString(Expression* expr)
        {
            if (expr->IsStringLiteral())
                return expr;
            ScopedPtrList<Expression> args(pointer_buffer());
            args.Add(expr);
            return factory()->NewCallRuntime(Runtime::kInlineToString, args,
                expr->position());
        }

        // Returns true if we have a binary expression between two numeric
        // literals. In that case, *x will be changed to an expression which is the
        // computed value.
        bool ShortcutNumericLiteralBinaryExpression(Expression** x, Expression* y,
            Token::Value op, int pos);

        // Returns true if we have a binary operation between a binary/n-ary
        // expression (with the same operation) and a value, which can be collapsed
        // into a single n-ary expression. In that case, *x will be changed to an
        // n-ary expression.
        bool CollapseNaryExpression(Expression** x, Expression* y, Token::Value op,
            int pos, const SourceRange& range);

        // Returns a UnaryExpression or, in one of the following cases, a Literal.
        // ! <literal> -> true / false
        // + <Number literal> -> <Number literal>
        // - <Number literal> -> <Number literal with value negated>
        // ~ <literal> -> true / false
        Expression* BuildUnaryExpression(Expression* expression, Token::Value op,
            int pos);

        // Generate AST node that throws a ReferenceError with the given type.
        V8_INLINE Expression* NewThrowReferenceError(MessageTemplate message,
            int pos)
        {
            return NewThrowError(Runtime::kNewReferenceError, message,
                ast_value_factory()->empty_string(), pos);
        }

        // Generate AST node that throws a SyntaxError with the given
        // type. The first argument may be null (in the handle sense) in
        // which case no arguments are passed to the constructor.
        V8_INLINE Expression* NewThrowSyntaxError(MessageTemplate message,
            const AstRawString* arg, int pos)
        {
            return NewThrowError(Runtime::kNewSyntaxError, message, arg, pos);
        }

        // Generate AST node that throws a TypeError with the given
        // type. Both arguments must be non-null (in the handle sense).
        V8_INLINE Expression* NewThrowTypeError(MessageTemplate message,
            const AstRawString* arg, int pos)
        {
            return NewThrowError(Runtime::kNewTypeError, message, arg, pos);
        }

        // Reporting errors.
        void ReportMessageAt(Scanner::Location source_location,
            MessageTemplate message, const char* arg = nullptr,
            ParseErrorType error_type = kSyntaxError)
        {
            pending_error_handler()->ReportMessageAt(source_location.beg_pos,
                source_location.end_pos, message,
                arg, error_type);
            scanner_.set_parser_error();
        }

        // Dummy implementation. The parser should never have a unidentifiable
        // error.
        V8_INLINE void ReportUnidentifiableError() { UNREACHABLE(); }

        void ReportMessageAt(Scanner::Location source_location,
            MessageTemplate message, const AstRawString* arg,
            ParseErrorType error_type = kSyntaxError)
        {
            pending_error_handler()->ReportMessageAt(source_location.beg_pos,
                source_location.end_pos, message,
                arg, error_type);
            scanner_.set_parser_error();
        }

        void ReportUnexpectedTokenAt(
            Scanner::Location location, Token::Value token,
            MessageTemplate message = MessageTemplate::kUnexpectedToken);

        // "null" return type creators.
        V8_INLINE static std::nullptr_t NullIdentifier() { return nullptr; }
        V8_INLINE static std::nullptr_t NullExpression() { return nullptr; }
        V8_INLINE static std::nullptr_t NullLiteralProperty() { return nullptr; }
        V8_INLINE static ZonePtrList<Expression>* NullExpressionList()
        {
            return nullptr;
        }
        V8_INLINE static ZonePtrList<Statement>* NullStatementList()
        {
            return nullptr;
        }
        V8_INLINE static std::nullptr_t NullStatement() { return nullptr; }
        V8_INLINE static std::nullptr_t NullBlock() { return nullptr; }
        Expression* FailureExpression() { return factory()->FailureExpression(); }

        template <typename T>
        V8_INLINE static bool IsNull(T subject)
        {
            return subject == nullptr;
        }

        // Non-null empty string.
        V8_INLINE const AstRawString* EmptyIdentifierString() const
        {
            return ast_value_factory()->empty_string();
        }

        // Producing data during the recursive descent.
        V8_INLINE const AstRawString* GetSymbol() const
        {
            const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory());
            DCHECK_NOT_NULL(result);
            return result;
        }

        V8_INLINE const AstRawString* GetIdentifier() const { return GetSymbol(); }

        V8_INLINE const AstRawString* GetNextSymbol() const
        {
            return scanner()->NextSymbol(ast_value_factory());
        }

        V8_INLINE const AstRawString* GetNumberAsSymbol() const
        {
            double double_value = scanner()->DoubleValue();
            char array[100];
            const char* string = DoubleToCString(double_value, ArrayVector(array));
            return ast_value_factory()->GetOneByteString(string);
        }

        class ThisExpression* ThisExpression()
        {
            UseThis();
            return factory()->ThisExpression();
        }

        Expression* NewSuperPropertyReference(int pos);
        Expression* NewSuperCallReference(int pos);
        Expression* NewTargetExpression(int pos);
        Expression* ImportMetaExpression(int pos);

        Expression* ExpressionFromLiteral(Token::Value token, int pos);

        V8_INLINE VariableProxy* ExpressionFromPrivateName(ClassScope* class_scope,
            const AstRawString* name,
            int start_position)
        {
            VariableProxy* proxy = factory()->ast_node_factory()->NewVariableProxy(
                name, NORMAL_VARIABLE, start_position);
            class_scope->AddUnresolvedPrivateName(proxy);
            return proxy;
        }

        V8_INLINE VariableProxy* ExpressionFromIdentifier(
            const AstRawString* name, int start_position,
            InferName infer = InferName::kYes)
        {
            if (infer == InferName::kYes) {
                fni_.PushVariableName(name);
            }
            return expression_scope()->NewVariable(name, start_position);
        }

        V8_INLINE void DeclareIdentifier(const AstRawString* name,
            int start_position)
        {
            expression_scope()->Declare(name, start_position);
        }

        V8_INLINE Variable* DeclareCatchVariableName(Scope* scope,
            const AstRawString* name)
        {
            return scope->DeclareCatchVariableName(name);
        }

        V8_INLINE ZonePtrList<Expression>* NewExpressionList(int size) const
        {
            return new (zone()) ZonePtrList<Expression>(size, zone());
        }
        V8_INLINE ZonePtrList<ObjectLiteral::Property>* NewObjectPropertyList(
            int size) const
        {
            return new (zone()) ZonePtrList<ObjectLiteral::Property>(size, zone());
        }
        V8_INLINE ZonePtrList<ClassLiteral::Property>* NewClassPropertyList(
            int size) const
        {
            return new (zone()) ZonePtrList<ClassLiteral::Property>(size, zone());
        }
        V8_INLINE ZonePtrList<Statement>* NewStatementList(int size) const
        {
            return new (zone()) ZonePtrList<Statement>(size, zone());
        }

        Expression* NewV8Intrinsic(const AstRawString* name,
            const ScopedPtrList<Expression>& args, int pos);

        V8_INLINE Statement* NewThrowStatement(Expression* exception, int pos)
        {
            return factory()->NewExpressionStatement(
                factory()->NewThrow(exception, pos), pos);
        }

        V8_INLINE void AddFormalParameter(ParserFormalParameters* parameters,
            Expression* pattern,
            Expression* initializer,
            int initializer_end_position,
            bool is_rest)
        {
            parameters->UpdateArityAndFunctionLength(initializer != nullptr, is_rest);
            auto parameter = new (parameters->scope->zone())
                ParserFormalParameters::Parameter(pattern, initializer,
                    scanner()->location().beg_pos,
                    initializer_end_position, is_rest);

            parameters->params.Add(parameter);
        }

        V8_INLINE void DeclareFormalParameters(ParserFormalParameters* parameters)
        {
            bool is_simple = parameters->is_simple;
            DeclarationScope* scope = parameters->scope;
            if (!is_simple)
                scope->MakeParametersNonSimple();
            for (auto parameter : parameters->params) {
                bool is_optional = parameter->initializer() != nullptr;
                // If the parameter list is simple, declare the parameters normally with
                // their names. If the parameter list is not simple, declare a temporary
                // for each parameter - the corresponding named variable is declared by
                // BuildParamerterInitializationBlock.
                scope->DeclareParameter(
                    is_simple ? parameter->name() : ast_value_factory()->empty_string(),
                    is_simple ? VariableMode::kVar : VariableMode::kTemporary,
                    is_optional, parameter->is_rest(), ast_value_factory(),
                    parameter->position);
            }
        }

        void DeclareArrowFunctionFormalParameters(
            ParserFormalParameters* parameters, Expression* params,
            const Scanner::Location& params_loc);

        Expression* ExpressionListToExpression(const ScopedPtrList<Expression>& args);

        void SetFunctionNameFromPropertyName(LiteralProperty* property,
            const AstRawString* name,
            const AstRawString* prefix = nullptr);
        void SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
            const AstRawString* name,
            const AstRawString* prefix = nullptr);

        void SetFunctionNameFromIdentifierRef(Expression* value,
            Expression* identifier);

        V8_INLINE void CountUsage(v8::Isolate::UseCounterFeature feature)
        {
            ++use_counts_[feature];
        }

        // Returns true iff we're parsing the first function literal during
        // CreateDynamicFunction().
        V8_INLINE bool ParsingDynamicFunctionDeclaration() const
        {
            return parameters_end_pos_ != kNoSourcePosition;
        }

        V8_INLINE void ConvertBinaryToNaryOperationSourceRange(
            BinaryOperation* binary_op, NaryOperation* nary_op)
        {
            if (source_range_map_ == nullptr)
                return;
            DCHECK_NULL(source_range_map_->Find(nary_op));

            BinaryOperationSourceRanges* ranges = static_cast<BinaryOperationSourceRanges*>(
                source_range_map_->Find(binary_op));
            if (ranges == nullptr)
                return;

            SourceRange range = ranges->GetRange(SourceRangeKind::kRight);
            source_range_map_->Insert(
                nary_op, new (zone()) NaryOperationSourceRanges(zone(), range));
        }

        V8_INLINE void AppendNaryOperationSourceRange(NaryOperation* node,
            const SourceRange& range)
        {
            if (source_range_map_ == nullptr)
                return;
            NaryOperationSourceRanges* ranges = static_cast<NaryOperationSourceRanges*>(source_range_map_->Find(node));
            if (ranges == nullptr)
                return;

            ranges->AddRange(range);
            DCHECK_EQ(node->subsequent_length(), ranges->RangeCount());
        }

        V8_INLINE void RecordBlockSourceRange(Block* node,
            int32_t continuation_position)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node, new (zone()) BlockSourceRanges(continuation_position));
        }

        V8_INLINE void RecordCaseClauseSourceRange(CaseClause* node,
            const SourceRange& body_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(node,
                new (zone()) CaseClauseSourceRanges(body_range));
        }

        V8_INLINE void RecordConditionalSourceRange(Expression* node,
            const SourceRange& then_range,
            const SourceRange& else_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node->AsConditional(),
                new (zone()) ConditionalSourceRanges(then_range, else_range));
        }

        V8_INLINE void RecordBinaryOperationSourceRange(
            Expression* node, const SourceRange& right_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(node->AsBinaryOperation(),
                new (zone())
                    BinaryOperationSourceRanges(right_range));
        }

        V8_INLINE void RecordJumpStatementSourceRange(Statement* node,
            int32_t continuation_position)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                static_cast<JumpStatement*>(node),
                new (zone()) JumpStatementSourceRanges(continuation_position));
        }

        V8_INLINE void RecordIfStatementSourceRange(Statement* node,
            const SourceRange& then_range,
            const SourceRange& else_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node->AsIfStatement(),
                new (zone()) IfStatementSourceRanges(then_range, else_range));
        }

        V8_INLINE void RecordIterationStatementSourceRange(
            IterationStatement* node, const SourceRange& body_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node, new (zone()) IterationStatementSourceRanges(body_range));
        }

        V8_INLINE void RecordSuspendSourceRange(Expression* node,
            int32_t continuation_position)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(static_cast<Suspend*>(node),
                new (zone())
                    SuspendSourceRanges(continuation_position));
        }

        V8_INLINE void RecordSwitchStatementSourceRange(
            Statement* node, int32_t continuation_position)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node->AsSwitchStatement(),
                new (zone()) SwitchStatementSourceRanges(continuation_position));
        }

        V8_INLINE void RecordThrowSourceRange(Statement* node,
            int32_t continuation_position)
        {
            if (source_range_map_ == nullptr)
                return;
            ExpressionStatement* expr_stmt = static_cast<ExpressionStatement*>(node);
            Throw* throw_expr = expr_stmt->expression()->AsThrow();
            source_range_map_->Insert(
                throw_expr, new (zone()) ThrowSourceRanges(continuation_position));
        }

        V8_INLINE void RecordTryCatchStatementSourceRange(
            TryCatchStatement* node, const SourceRange& body_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node, new (zone()) TryCatchStatementSourceRanges(body_range));
        }

        V8_INLINE void RecordTryFinallyStatementSourceRange(
            TryFinallyStatement* node, const SourceRange& body_range)
        {
            if (source_range_map_ == nullptr)
                return;
            source_range_map_->Insert(
                node, new (zone()) TryFinallyStatementSourceRanges(body_range));
        }

        // Generate the next internal variable name for binding an exported namespace
        // object (used to implement the "export * as" syntax).
        const AstRawString* NextInternalNamespaceExportName();

        ParseInfo* info() const { return info_; }

        std::vector<uint8_t>* preparse_data_buffer()
        {
            return &preparse_data_buffer_;
        }

        // Parser's private field members.
        friend class PreParserZoneScope; // Uses reusable_preparser().
        friend class PreparseDataBuilder; // Uses preparse_data_buffer()

        ParseInfo* info_;
        Scanner scanner_;
        Zone preparser_zone_;
        PreParser* reusable_preparser_;
        Mode mode_;

        SourceRangeMap* source_range_map_ = nullptr;

        friend class ParserTarget;
        friend class ParserTargetScope;
        ParserTarget* target_stack_; // for break, continue statements

        ScriptCompiler::CompileOptions compile_options_;

        // For NextInternalNamespaceExportName().
        int number_of_named_namespace_exports_ = 0;

        // Other information which will be stored in Parser and moved to Isolate after
        // parsing.
        int use_counts_[v8::Isolate::kUseCounterFeatureCount];
        int total_preparse_skipped_;
        bool allow_lazy_;
        bool temp_zoned_;
        ConsumedPreparseData* consumed_preparse_data_;
        std::vector<uint8_t> preparse_data_buffer_;

        // If not kNoSourcePosition, indicates that the first function literal
        // encountered is a dynamic function, see CreateDynamicFunction(). This field
        // indicates the correct position of the ')' that closes the parameter list.
        // After that ')' is encountered, this field is reset to kNoSourcePosition.
        int parameters_end_pos_;
    };

    // ----------------------------------------------------------------------------
    // Target is a support class to facilitate manipulation of the
    // Parser's target_stack_ (the stack of potential 'break' and
    // 'continue' statement targets). Upon construction, a new target is
    // added; it is removed upon destruction.

    class ParserTarget {
    public:
        ParserTarget(ParserBase<Parser>* parser, BreakableStatement* statement)
            : variable_(&parser->impl()->target_stack_)
            , statement_(statement)
            , previous_(parser->impl()->target_stack_)
        {
            parser->impl()->target_stack_ = this;
        }

        ~ParserTarget() { *variable_ = previous_; }

        ParserTarget* previous() { return previous_; }
        BreakableStatement* statement() { return statement_; }

    private:
        ParserTarget** variable_;
        BreakableStatement* statement_;
        ParserTarget* previous_;
    };

    class ParserTargetScope {
    public:
        explicit ParserTargetScope(ParserBase<Parser>* parser)
            : variable_(&parser->impl()->target_stack_)
            , previous_(parser->impl()->target_stack_)
        {
            parser->impl()->target_stack_ = nullptr;
        }

        ~ParserTargetScope() { *variable_ = previous_; }

    private:
        ParserTarget** variable_;
        ParserTarget* previous_;
    };

} // namespace internal
} // namespace v8

#endif // V8_PARSING_PARSER_H_
